package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"syscall"
	"time"
)

// humanizeBytes 将字节数转换为人类可读的字符串 (e.g., 1.5 GB)
func humanizeBytes(bytes uint64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := uint64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

// getDiskUsage 获取指定路径所在磁盘分区的使用情况
func getDiskUsage(path string) (total, used, free uint64, err error) {
	var stat syscall.Statfs_t
	err = syscall.Statfs(path, &stat)
	if err != nil {
		return 0, 0, 0, err
	}

	total = stat.Blocks * uint64(stat.Bsize)
	free = stat.Bavail * uint64(stat.Bsize)
	used = total - (stat.Bfree * uint64(stat.Bsize))

	return total, used, free, nil
}

// --- 关键修改 1: 更新 getDirectorySize 以同时返回大小和最新修改时间 ---
// getDirectoryInfo 递归计算指定目录的总大小和最新修改时间
func getDirectoryInfo(path string) (size uint64, latestModTime time.Time, err error) {
	err = filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) {
				log.Printf("警告: 路径 '%s' 不存在（已忽略）: %v\n", filePath, err)
				return nil
			}
			if os.IsPermission(err) {
				log.Printf("警告: 访问 '%s' 权限不足（已忽略）: %v\n", filePath, err)
				return filepath.SkipDir
			}
			log.Printf("错误: 处理 '%s' 时出错: %v\n", filePath, err)
			return nil
		}

		// 更新总大小
		if !info.IsDir() {
			size += uint64(info.Size())
		}

		// 更新最新修改时间
		if info.ModTime().After(latestModTime) {
			latestModTime = info.ModTime()
		}
		return nil
	})
	return size, latestModTime, err
}

// --- 关键修改 2: 更新并发函数以处理新的返回值 ---
// getSubdirInfoConcurrently 并发计算指定路径下每个直接子目录的大小和最新修改时间
func getSubdirInfoConcurrently(path string) (map[string]struct {
	size  uint64
	mtime time.Time
}, error) {
	subdirInfo := make(map[string]struct {
		size  uint64
		mtime time.Time
	})
	var wg sync.WaitGroup
	var mu sync.Mutex

	entries, err := os.ReadDir(path)
	if err != nil {
		return nil, err
	}

	for _, entry := range entries {
		if entry.IsDir() {
			wg.Add(1)
			subdirName := entry.Name()
			subdirPath := filepath.Join(path, subdirName)

			go func(name, p string) {
				defer wg.Done()
				// 调用更新后的函数
				size, mtime, err := getDirectoryInfo(p)
				if err != nil {
					log.Printf("警告: 计算子目录 '%s' 信息失败（已忽略）: %v\n", p, err)
					return
				}
				mu.Lock()
				subdirInfo[name] = struct {
					size  uint64
					mtime time.Time
				}{size: size, mtime: mtime}
				mu.Unlock()
			}(subdirName, subdirPath)
		}
	}

	wg.Wait()
	return subdirInfo, nil
}

// --- 关键修改 3: 更新 getTopFiles 以包含修改时间 ---
// getTopFiles 获取最大的 N 个文件，并包含其修改时间
func getTopFiles(path string, topN int) (totalSize uint64, topFiles []struct {
	path  string
	size  uint64
	mtime time.Time
}, err error) {
	fileInfoMap := make(map[string]struct {
		size  uint64
		mtime time.Time
	})

	err = filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
		if err != nil {
			if os.IsNotExist(err) {
				log.Printf("警告: 路径 '%s' 不存在（已忽略）: %v\n", filePath, err)
				return nil
			}
			if os.IsPermission(err) {
				log.Printf("警告: 访问 '%s' 权限不足（已忽略）: %v\n", filePath, err)
				return filepath.SkipDir
			}
			log.Printf("错误: 处理 '%s' 时出错: %v\n", filePath, err)
			return nil
		}

		if !info.IsDir() {
			size := uint64(info.Size())
			totalSize += size
			relPath, err := filepath.Rel(path, filePath)
			if err != nil {
				relPath = filePath
			}
			fileInfoMap[relPath] = struct {
				size  uint64
				mtime time.Time
			}{size: size, mtime: info.ModTime()}
		}
		return nil
	})

	if err != nil {
		log.Printf("警告: 遍历目录时存在部分错误，但已返回可用数据: %v\n", err)
	}

	// 转换为切片并排序
	for path, info := range fileInfoMap {
		topFiles = append(topFiles, struct {
			path  string
			size  uint64
			mtime time.Time
		}{path: path, size: info.size, mtime: info.mtime})
	}

	// --- 关键修改 4: 调整排序逻辑，按大小降序 ---
	sort.Slice(topFiles, func(i, j int) bool {
		return topFiles[i].size > topFiles[j].size
	})

	if len(topFiles) > topN {
		topFiles = topFiles[:topN]
	}

	return totalSize, topFiles, nil
}

func truncateString(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen-3] + "..."
}

func main() {
	topN := flag.Int("n", 10, "指定要显示的最大文件/子目录数量，默认10")
	maxLen := flag.Int("len", 60, "指定文件名/目录名最大显示长度，超过截断，默认60")
	flag.Parse()

	var targetPath string
	if flag.NArg() > 0 {
		targetPath = flag.Arg(0)
	} else {
		var err error
		targetPath, err = os.Getwd()
		if err != nil {
			log.Fatalf("无法获取当前工作目录: %v", err)
		}
	}

	if _, err := os.Stat(targetPath); os.IsNotExist(err) {
		log.Fatalf("错误: 目标路径 '%s' 不存在", targetPath)
	}

	fmt.Printf("--- 正在分析路径: %s ---\n\n", targetPath)

	fmt.Println("=== 磁盘分区空间占用情况 ===")
	total, used, free, err := getDiskUsage(targetPath)
	if err != nil {
		log.Printf("警告: 无法获取磁盘信息（已忽略）: %v\n", err)
	} else {
		fmt.Printf("  总容量: %s\n", humanizeBytes(total))
		fmt.Printf("  已用空间: %s (%.2f%%)\n", humanizeBytes(used), float64(used)/float64(total)*100)
		fmt.Printf("  可用空间: %s\n", humanizeBytes(free))
	}
	fmt.Println()

	fmt.Printf("=== 目录内容统计 (Top %d) ===\n", *topN)
	totalSize, topFiles, err := getTopFiles(targetPath, *topN)
	if err != nil {
		log.Printf("警告: 统计文件时存在部分错误，但已显示可用结果\n")
	}
	fmt.Printf("  目标路径总大小: %s\n", humanizeBytes(totalSize))
	fmt.Printf("  已统计文件数量: %d\n", len(topFiles))
	fmt.Println()

	// --- 关键修改 5: 优化打印格式以包含时间 ---
	fmt.Printf("=== 最大的 %d 个文件 ===\n", *topN)
	// 打印表头
	fmt.Printf("  %-*s %12s %s\n", *maxLen, "文件路径", "大小", "最近修改时间")
	for _, file := range topFiles {
		fmt.Printf("  %-*s %12s %s\n",
			*maxLen,
			truncateString(file.path, *maxLen),
			humanizeBytes(file.size),
			file.mtime.Format(time.RFC3339)) // 使用标准时间格式
	}
	fmt.Println()

	fmt.Printf("=== 直接子目录大小 (Top %d) ===\n", *topN)
	subdirInfo, err := getSubdirInfoConcurrently(targetPath)
	if err != nil {
		log.Printf("警告: 统计子目录时存在部分错误（已忽略）: %v\n", err)
	} else {
		var subdirs []struct {
			path  string
			size  uint64
			mtime time.Time
		}
		for dir, info := range subdirInfo {
			subdirs = append(subdirs, struct {
				path  string
				size  uint64
				mtime time.Time
			}{path: dir, size: info.size, mtime: info.mtime})
		}
		// --- 关键修改 6: 同样按大小排序子目录 ---
		sort.Slice(subdirs, func(i, j int) bool {
			return subdirs[i].size > subdirs[j].size
		})
		if len(subdirs) > *topN {
			subdirs = subdirs[:*topN]
		}
		// 打印表头
		fmt.Printf("  %-*s %12s %s\n", *maxLen, "目录名", "大小", "最近修改时间")
		for _, dir := range subdirs {
			fmt.Printf("  %-*s %12s %s\n",
				*maxLen,
				truncateString(dir.path, *maxLen),
				humanizeBytes(dir.size),
				dir.mtime.Format(time.RFC3339))
		}
	}

	fmt.Println("\n--- 分析完成（部分忽略错误已在日志中显示）---")
}
